1   /*
2    * Copyright (C) 2012 The Guava Authors
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package com.google.common.collect.testing.google;
18  
19  import com.google.common.collect.BiMap;
20  import com.google.common.collect.testing.AbstractTester;
21  import com.google.common.collect.testing.FeatureSpecificTestSuiteBuilder;
22  import com.google.common.collect.testing.MapTestSuiteBuilder;
23  import com.google.common.collect.testing.OneSizeTestContainerGenerator;
24  import com.google.common.collect.testing.PerCollectionSizeTestSuiteBuilder;
25  import com.google.common.collect.testing.SetTestSuiteBuilder;
26  import com.google.common.collect.testing.features.CollectionFeature;
27  import com.google.common.collect.testing.features.Feature;
28  import com.google.common.collect.testing.features.MapFeature;
29  import com.google.common.collect.testing.google.DerivedGoogleCollectionGenerators.BiMapValueSetGenerator;
30  import com.google.common.collect.testing.google.DerivedGoogleCollectionGenerators.InverseBiMapGenerator;
31  import com.google.common.collect.testing.google.DerivedGoogleCollectionGenerators.MapGenerator;
32  import com.google.common.collect.testing.testers.SetCreationTester;
33  
34  import junit.framework.TestSuite;
35  
36  import java.util.ArrayList;
37  import java.util.Collections;
38  import java.util.HashSet;
39  import java.util.List;
40  import java.util.Map;
41  import java.util.Map.Entry;
42  import java.util.Set;
43  
44  /**
45   * Creates, based on your criteria, a JUnit test suite that exhaustively tests a {@code BiMap}
46   * implementation.
47   *
48   * @author Louis Wasserman
49   */
50  public class BiMapTestSuiteBuilder<K, V>
51      extends PerCollectionSizeTestSuiteBuilder<BiMapTestSuiteBuilder<K, V>,
52              TestBiMapGenerator<K, V>, BiMap<K, V>, Map.Entry<K, V>> {
53    public static <K, V> BiMapTestSuiteBuilder<K, V> using(TestBiMapGenerator<K, V> generator) {
54      return new BiMapTestSuiteBuilder<K, V>().usingGenerator(generator);
55    }
56  
57    @Override
58    protected List<Class<? extends AbstractTester>> getTesters() {
59      List<Class<? extends AbstractTester>> testers =
60          new ArrayList<Class<? extends AbstractTester>>();
61      testers.add(BiMapPutTester.class);
62      testers.add(BiMapInverseTester.class);
63      testers.add(BiMapRemoveTester.class);
64      testers.add(BiMapClearTester.class);
65      return testers;
66    }
67  
68    enum NoRecurse implements Feature<Void> {
69      INVERSE;
70  
71      @Override
72      public Set<Feature<? super Void>> getImpliedFeatures() {
73        return Collections.emptySet();
74      }
75    }
76  
77    @Override
78    protected
79        List<TestSuite>
80        createDerivedSuites(
81            FeatureSpecificTestSuiteBuilder<?,
82                ? extends OneSizeTestContainerGenerator<BiMap<K, V>, Entry<K, V>>> parentBuilder) {
83      List<TestSuite> derived = super.createDerivedSuites(parentBuilder);
84      // TODO(cpovirk): consider using this approach (derived suites instead of extension) in
85      // ListTestSuiteBuilder, etc.?
86      derived.add(MapTestSuiteBuilder
87          .using(new MapGenerator<K, V>(parentBuilder.getSubjectGenerator()))
88          .withFeatures(parentBuilder.getFeatures())
89          .named(parentBuilder.getName() + " [Map]")
90          .suppressing(parentBuilder.getSuppressedTests())
91          .suppressing(SetCreationTester.class.getMethods())
92             // BiMap.entrySet() duplicate-handling behavior is too confusing for SetCreationTester
93          .createTestSuite());
94      /*
95       * TODO(cpovirk): the Map tests duplicate most of this effort by using a
96       * CollectionTestSuiteBuilder on values(). It would be nice to avoid that
97       */
98      derived.add(SetTestSuiteBuilder
99          .using(new BiMapValueSetGenerator<K, V>(parentBuilder.getSubjectGenerator()))
100         .withFeatures(computeValuesSetFeatures(parentBuilder.getFeatures()))
101         .named(parentBuilder.getName() + " values [Set]")
102         .suppressing(parentBuilder.getSuppressedTests())
103         .suppressing(SetCreationTester.class.getMethods())
104           // BiMap.values() duplicate-handling behavior is too confusing for SetCreationTester
105         .createTestSuite());
106     if (!parentBuilder.getFeatures().contains(NoRecurse.INVERSE)) {
107       derived.add(BiMapTestSuiteBuilder
108           .using(new InverseBiMapGenerator<K, V>(parentBuilder.getSubjectGenerator()))
109           .withFeatures(computeInverseFeatures(parentBuilder.getFeatures()))
110           .named(parentBuilder.getName() + " inverse")
111           .suppressing(parentBuilder.getSuppressedTests())
112           .createTestSuite());
113     }
114 
115     return derived;
116   }
117 
118   private static Set<Feature<?>> computeInverseFeatures(Set<Feature<?>> mapFeatures) {
119     Set<Feature<?>> inverseFeatures = new HashSet<Feature<?>>(mapFeatures);
120 
121     boolean nullKeys = inverseFeatures.remove(MapFeature.ALLOWS_NULL_KEYS);
122     boolean nullValues = inverseFeatures.remove(MapFeature.ALLOWS_NULL_VALUES);
123 
124     if (nullKeys) {
125       inverseFeatures.add(MapFeature.ALLOWS_NULL_VALUES);
126     }
127     if (nullValues) {
128       inverseFeatures.add(MapFeature.ALLOWS_NULL_KEYS);
129     }
130 
131     inverseFeatures.add(NoRecurse.INVERSE);
132     inverseFeatures.remove(CollectionFeature.KNOWN_ORDER);
133     inverseFeatures.add(MapFeature.REJECTS_DUPLICATES_AT_CREATION);
134 
135     return inverseFeatures;
136   }
137 
138   // TODO(user): can we eliminate the duplication from MapTestSuiteBuilder here?
139 
140   private static Set<Feature<?>> computeValuesSetFeatures(
141       Set<Feature<?>> mapFeatures) {
142     Set<Feature<?>> valuesCollectionFeatures =
143         computeCommonDerivedCollectionFeatures(mapFeatures);
144     valuesCollectionFeatures.add(CollectionFeature.ALLOWS_NULL_QUERIES);
145 
146     if (mapFeatures.contains(MapFeature.ALLOWS_NULL_VALUES)) {
147       valuesCollectionFeatures.add(CollectionFeature.ALLOWS_NULL_VALUES);
148     }
149 
150     valuesCollectionFeatures.add(CollectionFeature.REJECTS_DUPLICATES_AT_CREATION);
151 
152     return valuesCollectionFeatures;
153   }
154 
155   private static Set<Feature<?>> computeCommonDerivedCollectionFeatures(
156       Set<Feature<?>> mapFeatures) {
157     return MapTestSuiteBuilder.computeCommonDerivedCollectionFeatures(mapFeatures);
158   }
159 }